// Copyright 2012 dmitry.a.novikov@gmail.com
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "consistenz.h"
#include "valuestring.h"
#include <new>
#include "objectimpl.h"
#include <string.h>


#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )
#else
#define offsetof(s,m)   (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
#endif

namespace CnsZ {

  DEFINE_CNSZ_CLASS(ValueString, Object, NULL_FACTORY);

  const ValueString* ValueString::Empty() {
    CNSZ_DEFINE_STATIC_STRING(empty,L"");
    return CNSZ_GET_STATIC_STRING(empty);
  }

  class DynamicValueString : public ValueString {
  public:
    DynamicValueString* set(size_t l) { len=l; return this; }

    DynamicValueString* set(size_t l, const wchar_t* string) {
      len=l;
      ::memcpy(buffer, string, (len+1)*sizeof(wchar_t));
      return this;
    }

    inline static P<DynamicValueString> New(size_t length) {
      size_t addmem = sizeof(wchar_t)*(length+1);
      addmem -= sizeof(DynamicValueString) - offsetof(DynamicValueString, buffer); // not include buffer[1] and alignment padding
      return ImplRefCount<DynamicValueString>::New(addmem)->set(length);
    }
  };

  String ValueString::New(const wchar_t* string) {
    return New(string, ::wcslen(string));
  }

  String ValueString::New(const wchar_t* string, size_t length) {
    size_t addmem = sizeof(wchar_t)*(length+1);
    addmem -= sizeof(DynamicValueString) - offsetof(DynamicValueString, buffer); // not include buffer[1] and alignment padding
    return ImplRefCount<DynamicValueString>::New(addmem)->set(length, string);
  }

  int ValueString::compare(const Object* other) const {
    const Class* otherClass = other->isA();
    if(otherClass==ValueString::classInfo()) {
      return ::wcscmp(chars(), static_cast<const ValueString*>(other)->chars());
    }
    P<ValueString> otherValueString = castTo<ValueString>(other);
    if(otherValueString.get()) {
      return ::wcscmp(chars(), otherValueString->chars());
    }
    return Object::compare(other);
  }

  bool String::operator==(const wchar_t* string) const {
    return ::wcscmp(chars(), string) == 0;
  }

  bool String::operator<(const wchar_t* string) const {
    return ::wcscmp(chars(), string) < 0;
  }

  bool String::operator>(const wchar_t* string) const {
    return ::wcscmp(chars(), string) > 0;
  }

  String String::operator +(const ValueString* string) const {
    size_t string_length = string->length();
    size_t newLen = length() + string_length;
    P<DynamicValueString> data = DynamicValueString::New(newLen);
    ::memcpy(const_cast<wchar_t *>(data->chars()), chars(), length() * sizeof(wchar_t));
    ::memcpy(const_cast<wchar_t *>(data->chars()) + length(), string->chars(), (string_length+1) * sizeof(wchar_t) );
    return data.get();
  }

  String ValueString::toString() const {
    return this;
  }


  DEFINE_CNSZ_CLASS(ValueAString, Object, NULL_FACTORY);

  const ValueAString* ValueAString::Empty() {
    CNSZ_DEFINE_STATIC_ASTRING(empty,"");
    return CNSZ_GET_STATIC_ASTRING(empty);
  }

  class DynamicValueAString : public ValueAString {
  public:
    DynamicValueAString* set(size_t l) { len=l; return this; }

    DynamicValueAString* set(size_t l, const char* string) {
      len=l;
      ::memcpy(buffer, string, (len+1)*sizeof(char));
      return this;
    }

    inline static P<DynamicValueAString> New(size_t length) {
      size_t addmem = sizeof(char)*(length+1);
      addmem -= sizeof(DynamicValueAString) - offsetof(DynamicValueAString, buffer); // not include buffer[1] and alignment padding
      return ImplRefCount<DynamicValueAString>::New(addmem)->set(length);
    }
  };

  AString ValueAString::New(const char* string) {
    return New(string, ::strlen(string));
  }

  AString ValueAString::New(const char* string, size_t length) {
    size_t addmem = sizeof(char)*(length+1);
    addmem -= sizeof(DynamicValueAString) - offsetof(DynamicValueAString, buffer); // not include buffer[1] and alignment padding
    return ImplRefCount<DynamicValueAString>::New(addmem)->set(length, string);
  }

  int ValueAString::compare(const Object* other) const {
    const Class* otherClass = other->isA();
    if(otherClass==ValueAString::classInfo()) {
      return ::strcmp(chars(), static_cast<const ValueAString*>(other)->chars());
    }
    P<ValueAString> otherValueString = castTo<ValueAString>(other);
    if(otherValueString.get()) {
      return ::strcmp(chars(), otherValueString->chars());
    }
    return Object::compare(other);
  }

  bool AString::operator==(const char* string) const {
    return ::strcmp(chars(), string) == 0;
  }

  bool AString::operator<(const char* string) const {
    return ::strcmp(chars(), string) < 0;
  }

  bool AString::operator>(const char* string) const {
    return ::strcmp(chars(), string) > 0;
  }

  AString AString::operator +(const ValueAString* string) const {
    size_t string_length = string->length();
    size_t newLen = length() + string_length;
    P<DynamicValueAString> data = DynamicValueAString::New(newLen);
    ::memcpy(const_cast<char*>(data->chars()), chars(), length() * sizeof(char));
    ::memcpy(const_cast<char*>(data->chars()) + length(), string->chars(), (string_length+1) * sizeof(char) );
    return data.get();
  }

  String ValueAString::toString() const {
    P<DynamicValueString> data = DynamicValueString::New(length());
    size_t n;
    ::mbstowcs_s(&n, const_cast<wchar_t*>(data->chars()), data->length()+1, chars(), length());
    return data.get();
  }

}
